package com.only4play.practice.pay.payment.kit.plugin;

import cn.hutool.core.net.URLEncoder;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.nacos.shaded.com.google.gson.Gson;
import com.ijpay.core.enums.SignType;
import com.ijpay.core.enums.TradeType;
import com.ijpay.core.kit.WxPayKit;
import com.ijpay.wxpay.WxPayApi;
import com.ijpay.wxpay.model.UnifiedOrderModel;
import com.only4play.codegen.support.constants.ValidStatus;
import com.only4play.codegen.support.model.JsonObject;
import com.only4play.practice.common.enums.pay.PayModeType;
import com.only4play.practice.common.utils.OrderNoGenUtils;
import com.only4play.practice.common.utils.SpringContextUtils;
import com.only4play.practice.pay.payment.core.service.ITradePaymentOrderService;
import com.only4play.practice.pay.payment.core.entity.TradePaymentOrder;
import com.only4play.practice.pay.payment.kit.PaymentPlugin;
import com.only4play.practice.pay.payment.kit.request.PayParamRequest;
import com.only4play.practice.pay.reconcile.core.entity.MerchantApp;
import com.only4play.practice.pay.reconcile.core.model.payconfig.PayConfig;
import com.only4play.practice.pay.reconcile.core.model.payconfig.WechatPayConfig;
import com.only4play.practice.pay.reconcile.core.service.IMerchantAppService;
import lombok.extern.slf4j.Slf4j;

import javax.servlet.http.HttpServletRequest;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author mengdy
 */
@Slf4j
public class WechatPaymentPlugin implements PaymentPlugin {

    private final IMerchantAppService merchantAppService =
            SpringContextUtils.getBean(IMerchantAppService.class);

    private final ITradePaymentOrderService tradePaymentOrderService =
            SpringContextUtils.getBean(ITradePaymentOrderService.class);

    private final OrderNoGenUtils orderNoGenUtils =
            SpringContextUtils.getBean(OrderNoGenUtils.class);

    @Override
    public JsonObject<Object> unifiedOrder(HttpServletRequest request, PayParamRequest payParam) {

        if (!PayModeType.of(payParam.getPayModeType()).isPresent()){
            throw new IllegalArgumentException("支付类型不支持");
        }
        switch (PayModeType.of(payParam.getPayModeType()).get()){
            case WECHAT_PAY_PAYMENT_CODE:{
                throw new IllegalArgumentException("咱不支持该支付类型");
            }
            case WECHAT_PAY_JSAPI:{
                log.info("微信小程序支付.....");
                return null;
            }
            case WECHAT_PAY_NATIVE:{
                log.info("微信扫码支付.....");
                return nativePay(request,payParam);
            }
            default:{
                throw new IllegalArgumentException("支付类型不支持");
            }
        }
    }

    public JsonObject<Object> nativePay(HttpServletRequest request, PayParamRequest payParam) {

        log.info("native 微信扫码支付.....");
        // 根据渠道编码获取支付配置
        String cpId = payParam.getChannelPointCode();
        MerchantApp merchantApp = merchantAppService.findByChanPointId(cpId);
        log.info("商户配置信息:{}",merchantApp);
        if (ObjectUtil.isNull(merchantApp)){
            throw new IllegalArgumentException("商户支付配置信息不存在");
        }

        PayConfig payConfig = merchantApp.getPayConfig();
        WechatPayConfig wechatPayConfig = (WechatPayConfig) payConfig;
        log.info("微信支付配置信息:{}",wechatPayConfig);
        String genOrderNo = orderNoGenUtils.genOrderNo(payParam.getServiceName());
        TradePaymentOrder paymentOrder = createPaymentOrder(payParam,genOrderNo);
        log.info("创建支付流水订单:{}",paymentOrder);
        // TODO 创建支付流水订单
        boolean save = tradePaymentOrderService.save(paymentOrder);
        if (!save){
            throw new IllegalArgumentException("创建支付流水订单失败");
        }

        // TODO 构造微信支付网关请求参数
        UnifiedOrderModel.UnifiedOrderModelBuilder builder = UnifiedOrderModel.builder();
        if (ObjectUtil.equals(wechatPayConfig.getIsServiceProvider(), ValidStatus.VALID)){
            log.info("服务商支付.....");
            builder
                    .appid(wechatPayConfig.getAppId())
                    .mch_id(wechatPayConfig.getMchNo())
                    .sub_appid(wechatPayConfig.getSubAppId())
                    .sub_mch_id(wechatPayConfig.getSubMchNo())
                    .openid(payParam.getOpenId());
        }else {
            log.info("直连支付.....");
            builder
                    .appid(wechatPayConfig.getAppId())
                    .mch_id(wechatPayConfig.getMchNo())
                    .openid(payParam.getOpenId());
        }
        String attach =
        URLEncoder.createDefault().encode(JSONUtil.toJsonStr(payParam), StandardCharsets.UTF_8);
        Map<String, String> params = builder
                .nonce_str(WxPayKit.generateStr())
                .body(payParam.getDescBody())
                // 附加数据，微信会原样返回
                .attach(attach)
                .out_trade_no(paymentOrder.getChanTraPayOrderNo())
                .total_fee(String.valueOf(payParam.getPayAmount()))
                .spbill_create_ip(request.getRemoteAddr())
                .notify_url(wechatPayConfig.getDomain())
                .trade_type(TradeType.NATIVE.getTradeType())
                .build()
                .createSign(wechatPayConfig.getPartnerKey(), SignType.HMACSHA256);
        log.info("########## doNativeTrade pushOrder 入参:{} ", JSON.toJSONString(params));
        ;
        String xmlResult = WxPayApi.pushOrder(
                ObjectUtil.equals(wechatPayConfig.getIsSandbox(),ValidStatus.VALID),
                params);
        log.debug("Response from wechat: {}", xmlResult);
        Map<String, String> result = WxPayKit.xmlToMap(xmlResult);
        try {
            checkWechatRespBody(result);
        }catch (Exception e){
            //TODO 这里捕捉，后面再看怎么处理
            log.error("微信支付异常:{}",e.getMessage());
        }

        String qrCodeUrl = result.get("code_url");
        Map<String, String> packageParams = new HashMap<>();
        packageParams.put("qr_code_url",qrCodeUrl);
        log.info("Generate QRCodeTrade result: {}", new Gson().toJson(packageParams));
        return JsonObject.success(packageParams);
    }

    // *************************************************  //


    @Override
    public String unifiedCallback(HttpServletRequest request,Map<String, String> params) {



        return PaymentPlugin.super.unifiedCallback(request,params);
    }

    private void checkWechatRespBody(Map<String, String> resultMap) throws Exception {
        String resultMapString = resultMap.entrySet().stream()
                .map(entry -> entry.getKey() + ": " + entry.getValue())
                .collect(Collectors.joining(", "));

        log.debug("checkWechatRespBody resultMap = {}", resultMapString);

        String returnCode = resultMap.get("return_code");
        String returnMsg = resultMap.get("return_msg");
        if (!WxPayKit.codeIsOk(returnCode)) {
            log.error(String.format("Failed to make pay request to wechat: return_code = %s, return_msg = %s",
                    returnCode, returnMsg));
            //TODO 这里需要进一步解析returnCode为具体数字，方便定位问题
            throw new RuntimeException(returnMsg);
        }

        String resultCode = resultMap.get("result_code");
        if (!WxPayKit.codeIsOk(resultCode)) {
            String errorCode = resultMap.get("err_code");
            String errorCodeDes = resultMap.get("err_code_des");
            log.error(String.format("Failed to make pay request to wechat: result_code = %s, err_code=%s, err_code_des=%s",
                    resultCode, errorCode, errorCodeDes));
            throw new Exception(returnMsg);
        }
    }

}
